//General Purpose
using System;
using System.Collections.Generic;
using System.Text;

//For the project
using CS6238Project2.Banking.Communication;
using CS6238Project2.Banking.Commerce;
using CS6238Project2.Banking.Tools;

//For crypto
using System.Security.Cryptography.Xml;
using System.ComponentModel;
//For Serialization
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;


namespace CS6238Project2.Banking.Entity
{
    /// <summary>
    /// A customer of a bank.
    /// </summary>
    [Serializable]
    [XmlRoot]
    public class User
    {
        protected Account account;
        protected string name;
        protected BindingList<Certificate> certificates;
        protected BindingList<Good> goods;
        protected BindingList<Purchase> purchases;
        protected BindingList<AddressEntry> merchants;


        /// <summary>
        /// Initializes a user with some lists populated with defaults.
        /// </summary>
        public User()
        {
            goods = new BindingList<Good>();
            goods.Add(new Good("House", 100000));
            purchases = new BindingList<Purchase>();
            purchases.Add(new Purchase(goods[0], 1));

        }

        
        /// <summary>
        /// Instantiating a user with a name.
        /// </summary>
        /// <param name="n">Name of the user</param>
        public User(string n)
        {
            name = n;
            account = new Account();
            certificates = new BindingList<Certificate>();
            goods = new BindingList<Good>();


        }

        /// <summary>
        /// Processes received parcels and returns a parcel to be analyzed
        /// </summary>
        /// <param name="received">The received parcel</param>
        /// <returns>The parcel to be analyzed and acted on.</returns>
        public Parcel ProcessParcel(Parcel received)
        {
            

            try
            {
                //Check to make sure receiver and itself are the same
                if (received.Receiver != name)
                {
                    received.MessageData = "Parcel receiver was " + received.Receiver +
                                        ", but should be " + name;
                    received.Type = "Terminate";
                }


                if (received.Type == "Transaction Response")
                {
                    //says whether the transaction was approved
                    //Should be in the message data.
                    //NO TYPE NEEDED
                    received.Type = "Terminate";
                }
                else if (received.Type == "Order Response")
                {
                    received = CompleteOrder(received);//process order completion
                    //NO TYPE NEEDED
                    received.Type = "Terminate";
                }
                else if (received.Type == "Registration Response")
                {
                    received = Register(received); //process the registration completion
                    //NO TYPE NEEDED
                    received.Type = "Terminate";
                }
                else if (received.Type == "List Response")
                {
                    account = received.Account;
                    received.Type = "Terminate";
                }
                else
                {
                    received.MessageData = "Type " + received.Type + " not recognized";
                    received.Type = "Terminate";
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                received.MessageData = "Exception occurred.";
            }
            return received;

        }

        /// <summary>
        /// Produces an order to be sent to a merchant, enclosed in a 
        /// parcel.
        /// </summary>
        /// <param name="merchantName">The name of the merchant</param>
        /// <param name="goodName">The name of the good to be bought</param>
        /// <param name="quantity">The quantity of the items</param>
        /// <param name="value">The value of the items</param>
        /// <returns>A parcel containing purchase|transaction|verifying parcel</returns>
        public Parcel Order(string merchantName, string goodName, int quantity, int value)
        {
            /*
             * Parcel =
             * {
             * Certificate (defines for merchant how to take this apart)
             * Transaction 
             * Purchase 
             * Auth
             * }
             */
            Good good = new Good();
            good.Name = goodName;
            good.Worth = value;
            Parcel sendingParcel = new Parcel();
            sendingParcel.Source = name;
            Certificate bCert = new Certificate(certificates[0]);
            Certificate mCert = new Certificate(RetrieveCertificate(merchantName, true));
            DateTime timestamp = DateTime.Now;
            if (mCert.Issuer != "Certificate Not Found")
            {
                Certificate authCert = new Certificate();
                
                Transaction transaction = new Transaction
                    (merchantName, name, account.Number, value * quantity * -1, DateTime.Now);

                /*   GENERATE CERTIFICATE FOR BANK TO PROCESS AND APPROVE   */
                sendingParcel.Certificate = new Certificate(bCert);//Set the key to the bank/user key
                authCert = new Certificate(bCert);//set the auth cert to the bank cert
                authCert.e_Transaction(transaction); //set the transaction in the authcert
                authCert.TimeStamp = timestamp;
                sendingParcel.e_Authorization(new Certificate(authCert));//encrypt certificate with the bank/user key
                sendingParcel.Certificate = null; //delete the key from the parcel
                


                /*        GENERATE INFORMATION FOR MERCHANT          */
                sendingParcel.Certificate = new Certificate(mCert);//set the key to the user/merchant key
                sendingParcel.Transaction = transaction;//Set the plain transaction
                sendingParcel.e_Purchase(new Purchase(good, quantity)); //define the purchase


                sendingParcel.Certificate.Token = null; //remove token so not to reveal private key


                /*            NAME PARCEL!          */
                sendingParcel.Sender = name;
                sendingParcel.Receiver = merchantName;
                sendingParcel.Type = "Order";
            }
            else
            {
                sendingParcel.MessageData = "Merchant with name " + merchantName +
                                    " not found among certificates";
            }
            return sendingParcel;//send
        }


        /// <summary>
        /// Processes an order that was received.
        /// </summary>
        /// <param name="parcel">The parcel returned to user by the merchant</param>
        /// <returns>True if it's approved, false otherwise</returns>
        protected Parcel CompleteOrder(Parcel parcel)
        {
            /*    ASSURE ORDER WAS APPROVED  */
            if (parcel.MessageData != "Approved")
            {
                parcel.MessageData = "User not registered with " + parcel.MessageData;
            }
            else
            {
                parcel.Certificate.Token = RetrieveCertificate(parcel.Sender, true).Token;

                //Put the good in our inventory
                AddGood(parcel.d_Purchase().Good);
                parcel.MessageData = "Order Completed";
            }
            return parcel;
        }

        /// <summary>
        /// Adds a good to the personal inventory
        /// </summary>
        /// <param name="good">The good to be added</param>
        /// <returns>True if it succeeds, false otherwise</returns>
        public bool AddGood(Good good)
        {
            bool approved = true;
            return approved;
        }

        /// <summary>
        /// Withdrawl or Deposit function
        /// </summary>
        /// <param name="amount">The amount to be withdrawn/deposited</param>
        /// <returns>A certificate that can be serialized and sent</returns>
        public Parcel Transact(int amount)
        {
            //create a certificate with an encrypted transaction
            Certificate certificate = new Certificate(certificates[0]);//transaction to be sent
            Transaction transaction = 
                new Transaction(name, name, account.Number, amount, DateTime.Now);

            
            
            certificate.TimeStamp = transaction.Timestamp;
            certificate.e_Transaction(transaction);
            
            

            Parcel parcel = new Parcel();

            parcel.Sender = name;//set the sender to ourself
            parcel.Receiver = certificate.Issuer;//set the receiver to the bank
            parcel.Certificate = certificate;//set the certificate
            //parcel.e_Authorization(certificate);
            parcel.Transaction = transaction;
            parcel.Type = "Transaction";
            parcel.Source = name;
            parcel.Certificate.Token = null;
            return parcel;
        }
        


        /// <summary>
        /// Locates the certificate of the target and returns it.
        /// </summary>
        /// <param name="target">The name of the certificate issuer</param>
        /// <param name="matchToIssuer">Switch to match a cert to issuer(T) or subject(F)</param>
        /// <returns></returns>
        public Certificate RetrieveCertificate(string target, bool matchToIssuer)
        {
            Certificate foundCertificate = null;
            int i = 0;
            int index = -1;
            //
            if (certificates != null)
            {
                if (matchToIssuer)
                {
                    //naive search through an unsorted list, stops if it finds the item
                    while ((i < certificates.Count) && (index < 0))
                    {
                        if ((name == certificates[i].Subject)
                                 &&
                                 (target == certificates[i].Issuer))
                        {
                            foundCertificate = new Certificate(certificates[i]);
                        }
                        i++;
                    }
                }
                else
                {

                    while ((i < certificates.Count) && (index < 0))
                    {
                        if ((name == certificates[i].Issuer)
                                 &&
                                 (target == certificates[i].Subject))
                        {
                            foundCertificate = new Certificate(certificates[i]);
                        }
                        i++;
                    }
                }
            }
            return foundCertificate;
        }


        /// <summary>
        /// First step in registration creates a parcel of registration data.
        /// </summary>
        /// <param name="receiver">a merchant name or the bank</param>
        /// <returns>The parcel to be sent</returns>
        public Parcel Register(String receiver)
        {
            Parcel parcel = new Parcel(); 
            parcel.Source = name;
            if (RetrieveCertificate(receiver, false) == null)
            {
                parcel = new Parcel();
                parcel.Type = "Registration Request";
                parcel.MessageData = "Registration";
                parcel.Receiver = receiver;
                parcel.Sender = name;
                return parcel;
            }
            else
            {
                parcel = new Parcel();
                parcel.MessageData = "Already registered with this entity";
            }
            return parcel;
        }


        /// <summary>
        /// Second step in registration, user receives a parcel from the authority
        /// which determines approval and verifies that the sender is the bank, so forth.
        /// 
        /// Assumes that we know who we are registering with.
        /// </summary>
        /// <param name="parcel">The parcel that was sent to it.</param>
        protected Parcel Register(Parcel parcel)
        {
            
            //check if it's approved or disapproved
            if (parcel.MessageData == "Approved")
            {
                if (certificates == null)
                {
                    certificates = new BindingList<Certificate>();
                }
                if (certificates.Count == 0)
                {
                    account = parcel.Account;
                }
                Certificate cert = new Certificate();
                cert = new Certificate(parcel.Certificate);
                cert.Token = new Token(parcel.Certificate.Token.Key, parcel.Certificate.Token.IV);
                certificates.Add(cert);
            }
            else
            {
                //Just dump whatever the message data is later
            }
            return parcel;
        }

        /// <summary>
        /// The name assigned to the user
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        /// <summary>
        /// The User's list of certificates.  First is always the bank.
        /// </summary>
        /// 
        [XmlElement(IsNullable = true)]
        public BindingList<Certificate> Certificates
        {
            get {
                if (certificates == null)
                {
                    return null;
                }
                else
                {
                    return certificates;
                }
            }
            set { certificates = value; }
        }

        /// <summary>
        /// The user's goods list-- in this case, the list of goods in his
        /// good view.
        /// </summary>
        public BindingList<Good> Goods
        {
            get { return goods; }
            set { goods = value; }
        }

        /// <summary>
        /// The list of items the user has purchased
        /// </summary>
        public BindingList<Purchase> Purchases
        {
            get { return purchases; }
            set { purchases = value; }
        }

        /// <summary>
        /// The list of merchants the user is aware of.
        /// </summary>
        public BindingList<AddressEntry> Merchants
        {
            get { return merchants; }
            set { merchants = value; }
        }


        public Account Account
        {
            get { return account; }
            set { account = value; }
        }


    }



    public struct AddressEntry
    {
        private string name;
        private string ip;
        private int port;

        public string Name
        {
            get { return name; }
            set { name = value; }
        }
        public string IP
        {
            get { return ip; }
            set { ip = value; }
        }
        public int Port
        {
            get { return port; }
            set { port = value; }
        }

    }

}
